package com.lei.chinese_herbal_medicine.Service.Impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lei.chinese_herbal_medicine.Entity.*;
import com.lei.chinese_herbal_medicine.Mapper.*;
import com.lei.chinese_herbal_medicine.Service.ModelService;
import com.lei.chinese_herbal_medicine.Util.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.util.*;


/**
 * (Model)表服务实现类
 *
 * @author makejava
 * @since 2024-01-18 10:06:42
 */
@Service("modelService")
public class ModelServiceImpl implements ModelService {
    @Resource
    private ModelMapper modelMapper;
    @Resource
    private HerbMapper herbMapper;
    @Resource
    private LayerMapper layerMapper;
    @Resource
    private TrainDatasetMapper trainDatasetMapper;
    @Resource
    private TestDatasetMapper testDatasetMapper;
    @Resource
    private TrainDataMapper trainDataMapper;
    @Resource
    private ModelHerbLinkMapper modelHerbLinkMapper;
    @Resource
    private FlowMapper flowMapper;
    @Resource
    private HandleModelDataUtil modelDataUtil;
    @Value("${model.project_path}")
    private String projectPath;
    @Value("${model.basicModel_path_dir}")
    private String basicModelPathDir;
    @Value("${model.train_dataset_dir_path}")
    private String trainDatasetDirPath;
    @Value("${model.test_dataset_dir_path}")
    private String testDatasetDirPath;
    @Value( "${model.newModel_path_dir}")
    private String newModelPathDir;
    @Value("${model.train_data_abs_path}")
    private String trainDataAbsPath;
    @Value("${model.recognition_dir_path}")
    private String recognitionDirPath;

    @Override
    public boolean getModelData(String id, Result result){
        Map resultMap=new HashMap();
        boolean flag=true;
        Model model=new Model();
        model.setId(id);
        List<Model> models=modelMapper.selectByModel(model,null);
        if(models.size()==1){
            model=models.get(0);
            model.setLayearOne(layerMapper.selectByPrimaryKey(model.getLayearOneId()));
            model.setLayearTwo(layerMapper.selectByPrimaryKey(model.getLayearTwoId()));
            model.setLayearThree(layerMapper.selectByPrimaryKey(model.getLayearThreeId()));
            resultMap.put("targetModel",model);
            List<ModelHerbLink> modelHerbLinks=modelHerbLinkMapper.selectByModelId(id);
            List<Herb> herbs=herbMapper.selectHerbsByHerbAndPageData(new Herb(),null);
            Map<String,Herb> herbMap=new HashMap<>();
            for (Herb herb : herbs) {
                herbMap.put(herb.getId(),herb);
            }
            List<Map<String,String>> tableData=new ArrayList<>();
            for (ModelHerbLink modelHerbLink : modelHerbLinks) {
                Map map=new HashMap();
                Herb herb=herbMap.get(modelHerbLink.getHerbId());
                map.put("name",herb.getHerbName());
                map.put("latin_name",herb.getLatinName());
                map.put("accuracy_rate",modelHerbLink.getAccuracyRate());
                tableData.add(map);
            }
            resultMap.put("tableData",tableData);
            List<Flow> flows=flowMapper.selectByModelId(model.getId());
            resultMap.put("flowData",DataAnalysis.analysisFlows(flows));
            TrainData trainData=trainDataMapper.selectByModelId(id);
            ObjectMapper objectMapper = new ObjectMapper();
            File file= null;
            FileOutputStream outputStream=null;
            try {
                file = File.createTempFile("train_data",".txt");
                outputStream=new FileOutputStream(file);
                outputStream.write(trainData.getTrainData());
                Map trainDataMap = objectMapper.readValue(file,  Map.class);
                ArrayList accuracy_rate= (ArrayList) trainDataMap.get("accuracy_rate");
                ArrayList loss_rate= (ArrayList) trainDataMap.get("loss_rate");
                trainDataMap.put("accuracy_rate",accuracy_rate);
                trainDataMap.put("loss_rate",loss_rate);
                resultMap.put("trainDataMap",trainDataMap);
            } catch (IOException e) {
                flag=false;
                throw new RuntimeException(e);
            }finally {
                if(outputStream!=null){
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
        System.out.println(resultMap);
        result.setData(resultMap);
        return flag;
    }

    @Override
    @Transactional
    public boolean changeModelStatus(String targetUseModelId, String currentUseModelId) {
        Model model=null;
        if(currentUseModelId!=null&&!currentUseModelId.equals(targetUseModelId)){
            model=new Model();
            model.setStatus("0");
            model.setId(currentUseModelId);
            modelMapper.updateByPrimaryKeySelective(model);
        }
        model=new Model();
        model.setId(targetUseModelId);
        String status=modelMapper.selectByModel(model,null).get(0).getStatus();
        if("1".equals(status.trim())){
            status="0";
        }else{
            status="1";
        }
        model.setStatus(status);
        modelMapper.updateByPrimaryKeySelective(model);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class,timeout = 60)
    public boolean deleteModel(String id) {
        boolean flag=false;
        Model model=new Model();
        model.setId(id);
        List<Model> models=modelMapper.selectByModel(model,null);
        if(models.size()==1){
            model=models.get(0);
            //删除layer
            layerMapper.deleteByPrimaryKey(model.getLayearOneId());
            layerMapper.deleteByPrimaryKey(model.getLayearTwoId());
            layerMapper.deleteByPrimaryKey(model.getLayearThreeId());
            //删除train_data
            TrainData trainData=trainDataMapper.selectByModelId(id);
            trainDataMapper.deleteByPrimaryKey(trainData.getId());
            //删除model_herb_link
            List<ModelHerbLink> modelHerbLink= modelHerbLinkMapper.selectByModelId(id);
            for (ModelHerbLink herbLink : modelHerbLink) {
                modelHerbLinkMapper.deleteByPrimaryKey(herbLink.getId());
            }
            //删除flow
            flowMapper.deleteByModelId(id);
            //删除model
            modelMapper.deleteByPrimaryKey(id);
            flag=true;
        }else{
            flag=false;
        }
        return flag;
    }

    @Override
    public Map recognition(String userId,MultipartFile file) throws IOException {
        Map herbAndFlow=new HashMap();
        Flow flow=new Flow();
        flow.setId(UUIDUtil.fastSimpleUUID());
        flow.setDate(new Date());
        Model model=new Model();
        model.setStatus("1");
        List<Model> models=modelMapper.selectByModel(model,null);
        if(models.size()==1){
            model=models.get(0);
            flow.setModelId(model.getId());
            flow.setUserId(userId);
            flowMapper.insertSelective(flow);
            herbAndFlow.put("flow",flow);
            File f=new File(projectPath+recognitionDirPath);
            if(!f.exists()){
                f.mkdirs();
            }
            String imgName=UUIDUtil.fastSimpleUUID()+".jpg";
            String imgPath=projectPath+recognitionDirPath+"\\"+imgName;
            File img=new File(imgPath);
            OutputStream outputStream=new FileOutputStream(img);
            outputStream.write(file.getBytes());
            outputStream.flush();
            outputStream.close();
            String herbLatinName=JavaOperatePythonUtil.recognitionImage(imgPath);
            Herb herb=new Herb();
            herb.setLatinName(herbLatinName);
            List<Herb> herbs=herbMapper.selectHerbsByHerbAndPageData(herb,null);
            if(herbs.size()==1){
                herbAndFlow.put("message","图像识别成功！");
                herb=herbMapper.selectByPrimaryKey(herbs.get(0).getId());
                herbAndFlow.put("herb",herb);
            }else{
                herbAndFlow.put("message","图像识别失败！");
            }
            System.out.println(herbLatinName);
        }else{
            herbAndFlow.put("message","模型正在维护中，识别失败！");
            return herbAndFlow;
        }
        return herbAndFlow;
    }


    @Override
    public List<Model> selectByModel(Model model, PageData pageData) {
        List<Model> models=modelMapper.selectByModel(model,pageData);
        if(pageData!=null){
            pageData.setCount(modelMapper.selectCount());
        }
        return models;
    }
    @Async
    @Override
    public void handelModel(String basicModel, Model model) throws IOException {
        System.out.println(model);
        Map map=new HashMap<>();
        if(basicModel!=null&&!"".equals(basicModel.trim())){//有无指定基础模型
            Model basic = modelMapper.selectByPrimaryKey(basicModel);
            if(basic!=null){
                OutputStream outputStream=null;
                try{
                    map.put("is_new_model",false);
                    String basicModel_path=projectPath+basicModelPathDir+"\\"+basic.getModelName()+".pth";
                    File file=new File(basicModel_path);
                    if(!file.exists()){
                        outputStream=new FileOutputStream(file);
                        outputStream.write(basic.getModelData());
                        outputStream.flush();
                    }
                    map.put("basicModel_file_name",basic.getModelName());
                    map.put("basicModelPathDir",basicModelPathDir);
                }catch (IOException e){
                    System.out.println("输出基础模型到指定路径失败");
                    map.put("is_new_model",true);
                    System.out.println(e.getMessage());
                }finally {
                    if(outputStream!=null){
                        outputStream.close();
                    }
                }
            }else{
                map.put("is_new_model",true);
            }
        }else{
            map.put("is_new_model",true);
        }
        map.put("species_number",herbMapper.selectCount());
        List<Herb> herbs=herbMapper.selectHerbsByHerbAndPageData(new Herb(),null);
        for (Herb herb : herbs) {   //同步数据集
            List<TrainDataset> trainDatasets=trainDatasetMapper.selectTrainDatasetByHerbId(herb.getId());
            DatasetOutputUtil.TrainDatasetOutput(projectPath+trainDatasetDirPath,trainDatasets,trainDatasetMapper);
            List<TestDataset> testDatasets=testDatasetMapper.selectTestDatasetByHerbId(herb.getId());
            DatasetOutputUtil.TestDatasetOutput(projectPath+testDatasetDirPath,testDatasets,testDatasetMapper);
        }
        map.put("newModelPathDir",newModelPathDir);
        modelDataUtil.setModelApplication(model,map);//配置模型配置文件
        boolean is=JavaOperatePythonUtil.createOrReinforceModel();
        if(is){
            byte[] modelBytes=HandleModelDataUtil.readFileByBytes(projectPath+newModelPathDir+"\\"+model.getModelName()+".pth");
            model.setModelData(modelBytes);
            Layer layer=model.getLayearOne();
            String layerId= UUIDUtil.fastSimpleUUID();
            model.setLayearOneId(layerId);
            layer.setId(layerId);
            layerMapper.insert(layer);
            layer=model.getLayearTwo();
            layerId= UUIDUtil.fastSimpleUUID();
            model.setLayearTwoId(layerId);
            layer.setId(layerId);
            layerMapper.insert(layer);
            layer=model.getLayearThree();
            layerId= UUIDUtil.fastSimpleUUID();
            model.setLayearThreeId(layerId);
            layer.setId(layerId);
            layerMapper.insert(layer);
            Map train_data=modelDataUtil.getModelTrainData();
            ArrayList<String> accuracy_rate= (ArrayList) train_data.get("accuracy_rate");
            ArrayList<String> loss_rate= (ArrayList) train_data.get("loss_rate");
            if(accuracy_rate!=null&&accuracy_rate.size()>=1){
                model.setAccuracyRate( Float.valueOf(accuracy_rate.get(accuracy_rate.size()-1)));
                model.setLossRate(Float.valueOf(loss_rate.get(loss_rate.size()-1)));
            }
            HashMap<String,String> herb_accuracy_rate= (HashMap) train_data.get("herb_accuracy_rate");
            Map<String,String> herb=new HashMap<>();
            for (Herb h : herbs) {
                herb.put(h.getLatinName(),h.getId());
            }
            for (String key : herb_accuracy_rate.keySet()) {
                ModelHerbLink modelHerbLink=new ModelHerbLink();
                modelHerbLink.setId(UUIDUtil.fastSimpleUUID());
                modelHerbLink.setModelId(model.getId());
                modelHerbLink.setHerbId(herb.get(key));
                modelHerbLink.setAccuracyRate(Float.valueOf(String.valueOf(herb_accuracy_rate.get(key))));
                modelHerbLinkMapper.insert(modelHerbLink);
            }
            modelMapper.insert(model);
            TrainData trainData=new TrainData();
            trainData.setId(UUIDUtil.fastSimpleUUID());
            trainData.setModelId(model.getId());
            trainData.setTrainData(HandleModelDataUtil.readFileByBytes(projectPath+trainDataAbsPath));
            trainDataMapper.insert(trainData);
        }else{
            System.out.println("模型训练失败");
        }
    }


}


